#include "keyboard.h"

/**
 * 01.03.12 - Steven Hartin - Copied over from a previous project, added
 *                            comments and slighlty modified code to work with
 *                            this project.
 */

CKeyboard::CKeyboard() : CInputDevice(DEVICE_KEYBOARD)//,
    //m_arrDebounceTimes(256)
{
    /**
     * Constructor, ensure that we initialise the input device with the direct
     * input constant value representation of this device. Also set up the 
     * array to hold enough keys for debounce purposes.
     */
    //m_arrDebounceTimes.resize(256);
    for (int i(0); i < 256; ++i)
    {
        m_arrDebounceTimes.push_back(CTimer());
    }
}

void CKeyboard::setUp(LPDIRECTINPUT8 pDirectInput, HWND hwndMain)
{
    /**
     * Sets up the device, attempts to acquire a lock on the device and set
     * up memory to hold the data values.
     *
     * @param LPDIRECTINPUT8 A pointer tot he direct input device.
     * @param HWND hwndMain A handle to the window directx is running on.
     */
    if (FAILED(pDirectInput->CreateDevice(GUID_SysKeyboard, 
                                          getDevice(), 
                                          NULL)))
    {
        // Can't create the device.
        setActive(false);
        cleanUp();
    }
    else
    {
        if (FAILED((*getDevice())->SetDataFormat(&c_dfDIKeyboard)))
        {
            // We can't set the format of the eyboard.
            setActive(false);
            cleanUp();
        }
        else
        {
            if (FAILED((*getDevice())->SetCooperativeLevel(hwndMain, 
                                        DISCL_FOREGROUND | DISCL_NONEXCLUSIVE)))
            {
                // Can't obtain a lock on the keyboard
                setActive(false);
                cleanUp();
            }
            else
            {
                // Everything was successful.
                setActive(true);
            }
        }
    }
}

void CKeyboard::processState()
{
    /**
     * Processes the current state of the keyboard. This obtains any keys
     * which are currently pressed. This refershes the key buffer.
     */
    char buffer[256];
    HRESULT hr;

    if (! isActive())
    {
        // We have no  keyboard device
        return;
    }

    // Clear the keyboard data buffer - just in case.
    ZeroMemory(buffer, 256);
    
    hr = (*getDevice())->GetDeviceState(sizeof(buffer),
                                         (LPVOID)&buffer); 
    if FAILED(hr) 
    { 
        // If this failed, the device has probably been lost.
        // Check for (hr == DIERR_INPUTLOST) and attempt to reacquire it here.

        hr = (*getDevice())->Acquire();
        while (hr == DIERR_INPUTLOST)
        {
            hr = (*getDevice())->Acquire();
        }

        hr = (*getDevice())->GetDeviceState(sizeof(buffer),
                                             (LPVOID)&buffer);
    } 

    if (!FAILED(hr))
    {
        // If we can find the keyboard device
        
        setActive(true);

        // Create the event
        DEVICE_EVENT deviceEvent;
        memcpy(deviceEvent.charrKeyboardData, buffer, 256);
        deviceEvent.type = getType();
        deviceEvent.pDevice = this;
        
        // Call all the registered event listeners
        processCallbacks(deviceEvent);
    }
}

bool CKeyboard::keyPressed(const unsigned char charrBuffer[256], 
                           int nArrIndex) const
{
    /**
     * Tests to see if a key has been pressed.
     *
     * @param const char charrBuffer[256] A reference to the array containing
     *                                    the state of the keys
     * @param int nArrayIndex The key to test for key pressed.
     * @return bool Whether or not the key is currently pressed.
     */
    if (nArrIndex >= 256)
    {
        return false;
    }
    if (KEYDOWN(charrBuffer, nArrIndex))
    {
        //MessageBox(NULL, "BUTTON PRESSED", "BUTTON PRESSED", MB_OK);
        // Key has been pressed.
        if (m_arrDebounceTimes.at(nArrIndex).getTimeDifference() >= 
            DEBOUNCE_TIME)
        {
            // Key is not under debounce time.
            return true;
        }
    }
    return false;
}

void CKeyboard::registerKeyDebounce(int nArrIndex, const CTimer &timeDiff)
{
    /**
     * Registers a key to have a doubounce timer associated with it. Ensures
     * that a key won't register as being hit more than once every so often.
     *
     * @param int nArrIndex The key to register the debounce time.
     * @param const CTimer &timeDiff A reference to a timer representing the 
     *                               time to allow a key to be classed as
     *                               'pressable' again.
     */
    try
    {
        m_arrDebounceTimes.at(nArrIndex).setTime(timeDiff.get_time());
    }
    catch (std::out_of_range)
    {
        // Hide any out of range exceptions. Just do nothing, no bad can
        // come of this.
    }
}